#include "decoder.hpp"

int Node::nodeCount = 0;
int Session::sessionCount = 0;

// ==================================================
//  Variable (NDecBox) Functions
// ==================================================

void NDecBox::setData (void *p, size_t l, BoxType t)
{
  if (this->data)
    delete this->data;
    
  this->data = new byte_t [l]; 
  this->len = l;
  this->type = t;

  memcpy (this->data, p, l);
  return ;
}

void NDecBox::boxTimeout (void * obj)
{
  NDecBox * box = static_cast<NDecBox*> (obj);
  assert (box->boxTable);
  box->boxTable->remove (&box->key, sizeof (boxkey_t));
}

void NDecBox::attach (NDecBox * prev)
{
  NDecBox * next = prev->next;
  prev->next = this;
  next->prev = this;
  this->next = next;
  this->prev = prev;
}

void NDecBox::detach ()
{
  NDecBox * next = this->next;
  NDecBox * prev = this->prev;
  next->prev = prev;
  prev->next = next;
  this->next = this->prev = NULL;
}



// ==================================================
//   NetworkObject Functions
// ==================================================

NetworkObject::~NetworkObject ()
{
  if (this->boxTable)
    delete this->boxTable;
  if (this->boxRoot)
    delete this->boxRoot;      
}

inline Table<NDecBox> * NetworkObject::setupTable ()
{
  if (this->boxTable == NULL)
    {
      this->boxTable = new PTree<NDecBox> ;
      this->boxRoot = new NDecBox;
      this->boxRoot->next = this->boxRoot;
      this->boxRoot->prev = this->boxRoot;    
      this->boxPtr = NULL;
    }

  return this->boxTable;
}
NDecBox * NetworkObject::lookupBox (boxkey_t &key)
{
  Table<NDecBox> * t = this->setupTable ();
  return t->lookup (&key, sizeof (boxkey_t));
}

NDecBox * NetworkObject::fetchBox (boxkey_t &key, int ttl)
{
  Table<NDecBox> * t = this->setupTable ();
  NDecBox * b = t->fetch (&key, sizeof (boxkey_t)); 
  b->key = key;
  b->boxTable = t;

  if (! b->isAttached ())
    b->attach (this->boxRoot);

  if (ttl > 0)
    {
      assert (this->timer); 
      // This function should be called after setTimer ()

      if (b->tnode)
	this->timer->resetTimer (b->tnode, ttl);
      else
	b->tnode = this->timer->setTimer (NDecBox::boxTimeout, b, ttl);
    }

  return b;
}

bool NetworkObject::removeBox (boxkey_t &key)
{
  if (this->boxTable == NULL)
    return false;

  return this->boxTable->remove (&key, sizeof (boxkey_t)); 
}

int NetworkObject::getBoxCount ()
{
  if (this->boxTable)
    return this->boxTable->getCount ();
  else
    return 0;
}

void NetworkObject::feBoxInit ()
{
  if (this->boxRoot)
    this->boxPtr = this->boxRoot->next;
  else
    this->boxPtr = NULL;

}

void NetworkObject::feBoxNext ()
{
  if (this->boxPtr)
    this->boxPtr = this->boxPtr->next;
}

bool NetworkObject::feBoxEnd ()
{
  if (this->boxPtr && this->boxPtr != this->boxRoot)
    return false;
  else
    return true;
}

boxkey_t NetworkObject::feBoxID ()
{
  if (this->boxPtr)
    return this->boxPtr->key;
  else
    return 0;
}

// ==================================================
//   Session Functions
// ==================================================

void Session::joinTable (Table<Session> &tree, void * key, int len)
{
  bool ret = tree.insert (key, len, this);
  assert (ret);
  this->ssnTable = &tree;
  this->setKey (key, len);
}

void Session::setKey (void * argKey, int argLen)
{
  assert (argKey && argLen > 0);
  this->key = malloc (argLen);
  this->keyLen = argLen;
  memcpy (this->key, argKey, argLen);
}

void Session::setNode (Node * l, Node * r)
  {
    assert (this->LNode == NULL && this->RNode == NULL);
    this->LNode = l;
    this->RNode = r;
    l->retain ();
    r->retain ();
  };

void Session::setTimeout (Timer &timer, int ttl)
{
  assert (this->ssnTable && this->key);
  this->setTimer (timer);

  if (this->tnode)
    timer.resetTimer (this->tnode, ttl);
  else
    this->tnode = timer.setTimer (ssnTimeout, this, ttl);
}

void Session::ssnTimeout (void * obj)
{
  Session * ssn = static_cast <Session*> (obj);
  ssn->ssnTable->remove (ssn->getKey (), ssn->getKeyLen ());
}

// ==================================================
//   Node Functions
// ==================================================

void Node::joinTable (Table<Node> &table, void * key, size_t len)
{
  bool ret = table.insert (key, len, this);
  assert (ret);
  this->nodeTable = &table;

  if (this->keyData)
    delete [] this->keyData;

  this->keyData = new byte_t [len];
  this->keyLen  = len;
  memcpy (this->keyData, key, len);  
}

void Node::removeSelf () 
{
  if (this->nodeTable)
    {
      assert (this->keyData && this->keyLen > 0);
      this->nodeTable->remove (this->keyData, this->keyLen);
    }
}

bool Node::isLocked ()
{
  if (this->getBoxCount () > 0)
    {
      assert (this->timer);
      if (this->tnode)
	{
	  debug (DEBUG_VAR, "locked by VARIABLE (%p) => Reset", this);
	  this->timer->resetTimer (this->tnode, 30);
	}
      else
	{
	  debug (DEBUG_VAR, "locked by VARIABLE (%p) => Install", this);
	  this->tnode = this->timer->setTimer (nodeTimeout, this, 30);
	}

      return true;
    }
  else
    {
      if (this->lock > 0)
	{
	  if (this->tnode)
	    {
	      this->tnode->disable ();
	      this->tnode = NULL;
	    }

	  return true;
	}
      else
	{
	  return false;
	}
    }
}

void Node::nodeTimeout (void * obj)
{
  Node * node = static_cast<Node*> (obj);
  if (! node->isLocked ())
    {
      debug (DEBUG_VAR, "garbage collect!");
      node->nodeTable->remove (node->keyData, node->keyLen);
    }
}

//==================================================
//  Record Functions
//==================================================

void Record::setData (DecType type, void * ptr, int len)
{
  this->len = len;
  this->ptr = ptr;
  this->type = type;
}

bool Record::lock ()
{
  if (this->locking)
    return false;
  else
    {
      this->locking = true;
      this->init ();
      return true;
    }
}

bool Record::unlock ()
{
  if (this->locking)
    {
      this->locking = false;
      return true;
    }
  else
    return false;
}



bool Record::setSessionRoot (SessionRoot * root)
{
  if (! this->rootPtr)
    return false;
  else
    {
      *this->rootPtr = root;
      return true;
    }
}

SessionRoot * Record::getSessionRoot (void)
{
  if (! this->rootPtr)
    return NULL;
  else
    return *this->rootPtr;
}




// ==================================================
//  NDecResult (Netdec Result)
// ==================================================

bool NDecResult::setPacket (Packet &p)
{
  if (this->lock)
    return false;

  this->pkt = p;
  return true;
}

Packet * NDecResult::getPacket (void)
{
  return &this->pkt;
}

bool NDecResult::push (Record * rec)
{
  this->lock = true;
  if (this->sp >= REC_MAX)
    return false;

  this->recStack [this->sp] = rec;
  this->recArray [rec->type] = rec;
  this->recCount [rec->type]++;
  this->sp++;
  return true;
}

void NDecResult::release (void)
{
  this->lock = false;
  if (this->sp == 0)
    return ;

  while (this->sp > 0)
    {
      this->sp--;
      this->recStack[this->sp]->release ();
      this->recArray [this->recStack[this->sp]->type] = NULL;
      this->recCount [this->recStack[this->sp]->type] = 0;
      this->recStack[this->sp] = NULL;
    }

  // memset (this->recArray, 0, sizeof (this->recArray));
  // memset (this->recCount, 0, sizeof (this->recCount));
  
  return ;
}


Record * NDecResult::getRecord (DecType type)
{
  assert (0 <= type && type < DECODER_COUNT);
  return this->recArray [type];
}

Record * NDecResult::getRecord (int idx)
{
  if (REC_MAX <= idx)
    {
      assert (0);
      return NULL;
    }

  return this->recStack [idx];
}

Node * NDecResult::getSrcNode ()
{
  for (int i = this->sp - 1; i >= 0; i--)
    {
      Record * r = this->recStack [i];
      if (r->type == DEC_IPV4 || r->type == DEC_IPV6)
	return r->getSrcNode ();
    }

  return NULL;
}

Node * NDecResult::getDstNode ()
{
  for (int i = this->sp - 1; i >= 0; i--)
    {
      Record * r = this->recStack [i];
      if (r->type == DEC_IPV4 || r->type == DEC_IPV6)
	return r->getDstNode ();
    }

  return NULL;
}

Session * NDecResult::getNetworkSession ()
{
  for (int i = this->sp - 1; i >= 0; i--)
    {
      Record * r = this->recStack [i];
      if (r->type == DEC_IPV4 || r->type == DEC_IPV6)
	return r->getSession ();
    }

  return NULL;
}

Session * NDecResult::getTransportSession ()
{
  for (int i = this->sp - 1; i >= 0; i--)
    {
      Record * r = this->recStack [i];
      if (r->type == DEC_TCP || r->type == DEC_UDP)
	return r->getSession ();
    }

  return NULL;
}


// ===================================================
//  NDecFilter
// ===================================================

bool NDecFilter::exec (NDecResult &res)
{
  this->initResult ();
  this->check (res);
  return (this->getResCount () > 0) ? true : false;
}

void NDecFilter::pushResult (void * v, int s) 
{  
  FilterResult * fres = this->resTable.fetch (&this->stack, sizeof (this->stack));
  fres->ptr = v;
  fres->len = s;
  this->stack++;
}

void * NDecFilter::getResult (size_t * s, int idx) 
{
  FilterResult * fres = this->resTable.lookup (&idx, sizeof (idx));
  if (fres)
    {
      if (s)
	*s = fres->len;

      return fres->ptr;
    }
  else
    return NULL;
}



// ===================================================
//  NDecMpxFilter
// ===================================================

void * NDecMpxFilter::getResult (int idx) 
{
  return ((this->mpxRes) ? this->mpxRes->lookup (idx) : NULL);
}


// ===================================================
//  NDecMpxRes
// ===================================================

NDecMpxRes::~NDecMpxRes () 
{
  if (this->array)
    free (this->array);
}

void NDecMpxRes::addItem (void * p)
{
  this->count++;
  this->array = (void**)realloc (this->array, sizeof (void *) * this->count);
  this->array [this->count - 1] = p;
}


void * NDecMpxRes::lookup (int idx)
{
  assert (idx >= 0);
  if (this->count <= idx)
    return NULL;
  else
    return this->array [idx];
}

// ===================================================
//  Decoder Functions
// ===================================================

	// void insertSession (Session &ssn, PTree<Session> &tree, void * k1, void * k2, int len)
	// {
	// }
	// 
void * Decoder::makeSsnKey (void * k1, void * k2, int len)
{
  if (this->ssnKeyLen != len * 2)
    {
      if (this->ssnKey)
	free (this->ssnKey);

      this->ssnKeyLen = len * 2;
      this->ssnKey = malloc (this->ssnKeyLen);
    }
  
  if (memcmp (k1, k2, len) < 0)
    {
      memcpy ((u_int8_t*)this->ssnKey,       k1, len);
      memcpy ((u_int8_t*)this->ssnKey + len, k2, len);
    }
  else
    {
      memcpy ((u_int8_t*)this->ssnKey,       k2, len);
      memcpy ((u_int8_t*)this->ssnKey + len, k1, len);
    }

  return this->ssnKey;
}


